#include <stdio.h>
#include <stdlib.h>
#define NIL_PROC NULL

struct proceso {
	int id;
	int priority;
	long penalty;
	struct proceso *p_nextready;
}; 

struct proceso *cabeza = NULL; /* reemplaza a rdy_head */
struct proceso *cola = NULL;   /* reemplaza a rdy_tail */
/* NULL reemplaza a NIL_PROC */
struct proceso* ubicarProceso(struct proceso*);

void main() {
  struct proceso *proceso1, *proceso2, *proceso3, *proceso4, *proceso5;
  struct proceso *aux;
  char tecla = 0;
  int i = 0;
  
  proceso1 = (struct proceso*)malloc(sizeof(struct proceso));
  proceso1->priority = 0x0F;
  proceso1->penalty = 0;
  proceso1->id = 1;

  proceso2 = (struct proceso*)malloc(sizeof(struct proceso));
  proceso2->priority = 0x0F;
  proceso2->penalty = 0;
  proceso2->id = 2;

  proceso3 = (struct proceso*)malloc(sizeof(struct proceso));
  proceso3->priority = 0x0F;
  proceso3->penalty = 0;
  proceso3->id = 3;

  /*proceso4 = (struct proceso*)malloc(sizeof(struct proceso));
  proceso4->priority = 0x07;
  proceso4->penalty = 0;
  proceso4->id = 4;

  proceso5 = (struct proceso*)malloc(sizeof(struct proceso));
  proceso5->priority = 0x02;
  proceso5->penalty = 0;
  proceso5->id = 5; */

  
  ready(proceso1);
  /*sched();
  ready(proceso2);
  sched();
  ready(proceso3);
  sched();
  ready(proceso4);
  sched();
  ready(proceso5);
  sched();*/

  while(1) {
	sleep(1);
	sched();

	if (i == 5)
		ready(proceso2);
	
	if (i == 10) {
		proceso2->priority = 0x0A;
		proceso2->penalty = 0;
		ready(proceso3);
	}

	if (i == 15) {
		aux = cabeza;
		if (cabeza->id == 1)
			cabeza = cabeza->p_nextready;
		else {
			while (aux->p_nextready->id != 1)
				aux = aux->p_nextready;

			aux->p_nextready = aux->p_nextready->p_nextready;
		}
	}

	i++;
  }
}

void pick_proc() {
	printf("(pick_proc) Se ejecutó el proceso %d\n", cabeza->id);
	imprimirCola();
}

void sched () {
  char tecla = 0;

  printf("sched()\n");

  	/* Esta parte del código simula a la función sched */

	/* Nos fijamos si hay un sólo proceso ejecutándose. En ese caso
	 * no hace falta aumentar un contador */
	if (cabeza == NULL) return;

	if (cabeza->p_nextready == NULL) {
		pick_proc();
		return;
	}

	/* Aumentamos la penalidad del proceso que se estuvo ejecutando */
	cabeza->penalty += cabeza->priority;

	printf("(sched) 1\n");

	/* Si el proceso que le sigue ahora tiene mayor prioridad, entonces
	 * empezamos un proceso de reacomodación */
	if (cabeza->p_nextready->penalty + cabeza->p_nextready->priority <
		cabeza->priority + cabeza->penalty) {

		struct proceso* cp = cabeza;
		cabeza = cabeza->p_nextready;
		ubicarProceso(cp);
	}

	printf("Fin sched()\n");
	pick_proc();
	sleep(1);
}

void ready(struct proceso *rp){

  struct proceso *aux;

  printf("ready(%d)\n", rp->id);

  /* Si la cola esta vacia, no importa la prioridad, lo insertamos
   * al principio */
  if (cabeza == NULL) {
		rp->p_nextready = NULL;
		cabeza = cola = rp;
  }
	else
		ubicarProceso(rp);

  printf("Fin ready(%d)\n", rp->id);
}

void imprimirCola() {
  struct proceso *aux = cabeza;

  if(cabeza == NULL) printf("La cola esta vacia\n");
  else {
	while(aux != NULL) {
		printf("|Pri-Pen %d-%ld|I %d|--> ",aux->priority, aux->penalty, aux->id);
		aux = aux->p_nextready;
	}
	printf("NULL\n");
  }
}

/*==========================================================================*
 *															ubicarProceso																*
 *==========================================================================*/
struct proceso* ubicarProceso(struct proceso* unProceso) {
/* Busca el proceso en la lista de procesos listos que deberia apuntar a
 * unProceso y devuelve un puntero a el. Si unProceso tiene una prioridad
 * inferior a rdy_head[USER_Q] entonces devuelve NIL_PROC. El llamador debe
 * encargarse de interpretar correctamente esto.
 */

  struct proceso *aux;

	/* Si la cola esta vacia, retornamos NULL */
	if (cabeza == NIL_PROC)
		return NIL_PROC;

  /* Si el proceso unProceso debe ubicarse al comienzo de la lista, entonces
   * retornamos NULL */
  if (cabeza->priority + cabeza->penalty >
				unProceso->priority + unProceso->penalty) {
		unProceso->p_nextready = cabeza;
		cabeza = unProceso;
		return;
  }

  /* En caso contrario buscamos al proceso anterior a unProceso */
  aux = cabeza;
  while ((aux->p_nextready != NIL_PROC) && 
          (unProceso->priority + unProceso->penalty >
            aux->p_nextready->priority + aux->p_nextready->penalty))
    aux = aux->p_nextready;
	
  if (aux->p_nextready == NIL_PROC) {
    aux->p_nextready = unProceso;
		unProceso->p_nextready = NIL_PROC;
		cola = unProceso;
	}
	else {
		unProceso->p_nextready = aux->p_nextready;
		aux->p_nextready = unProceso;
	}
}
